﻿// HSS.Forms.AppUserConfiguration.cs
// ----------------------------------------------------------------------------
// Licensed under the MIT license
// http://www.opensource.org/licenses/mit-license.html
// ----------------------------------------------------------------------------
// HighSpeed-Solutions, LLC
// Copyright (c) 2001-2010
// ----------------------------------------------------------------------------
// File:       AppUserConfiguration.cs
// Author:     HSS\gbanta
// Created:    08/12/2010
// Modified:   12/24/2010
// ----------------------------------------------------------------------------
namespace HSS.Forms
{
	#region Using Directives
	using System;
	using System.Collections.Generic;
	using System.Collections.ObjectModel;
	using System.Linq;
	using System.Windows.Forms;
	using System.Collections;
	#endregion

	#region AppUserConfiguration
	/// <summary>
	/// AppUserConfiguration{T} allows for the creation and persistence
	/// of <see cref="IConfigurationElement"/> objects for the current 
	/// application user. The configuration objects are persisted to the
	/// <see cref="AppUserData"/> file.
	/// </summary>
	public static class AppUserConfiguration<T> where T : IConfigurationElement, new()
	{
		#region Fields
		static string itemListKey;
		static Dictionary<string, T> items;
		private static readonly object itemsLock = new object();
		#endregion

		#region Constructors
		/// <summary>
		/// Constructor
		/// </summary>
		static AppUserConfiguration()
		{
			AutoSave = true;
			itemListKey = "AppUserConfiguration__" + typeof(T).FullName;
			IsSecured = typeof(T).GetInterface(typeof(ISecuredConfigurationElement).FullName, false) != null;
		}
		#endregion

		#region Properties
		/// <summary>
		/// Gets a read only collection of the currently available items.
		/// </summary>
		public static ReadOnlyCollection<T> Items
		{
			get
			{
				lock (itemsLock)
				{
					if (null == items)
						items = new Dictionary<string, T>();
					return items.Values.ToList().AsReadOnly();
				}
			}
		}
		/// <summary>
		/// Gets an array containing the currently available items.
		/// </summary>
		public static T[] ItemsArray
		{
			get
			{
				lock (itemsLock)
				{
					if (null == items)
						items = new Dictionary<string, T>();
					return items.Values.ToArray();
				}
			}
		}
		/// <summary>
		/// Gets an ArrayList containing the currently available items.
		/// </summary>
		public static ArrayList ItemsArrayList
		{
			get
			{
				lock (itemsLock)
				{
					if (null == items)
						items = new Dictionary<string, T>();
					return new ArrayList(items.Values);
				}
			}
		}
		/// <summary>
		/// Gets if the items are encrypted when persisted.
		/// </summary>
		public static bool IsSecured
		{
			get;
			private set;
		}
		/// <summary>
		/// Gets or sets whether or not adding/removing items
		/// will automatically save changes. If false, you will
		/// have to call Save from your code. Default: true.
		/// </summary>
		public static bool AutoSave
		{
			get;
			set;
		}
		/// <summary>
		/// Gets or sets the Editor to use for calls to
		/// Edit and CreateNew.
		/// </summary>
		public static Type Editor
		{
			get;
			set;
		}
		#endregion

		#region Methods
		/// <summary>
		/// Loads any previously saved items for the currently logged in user.
		/// </summary>
		public static void Refresh()
		{
			lock (itemsLock)
			{
				if (null == items)
					items = new Dictionary<string, T>();
				else
					items.Clear();

				var itemList = AppUserData.Get<AppData>(itemListKey, null);
				if (null == itemList)
					return;
				foreach (var key in itemList.Keys)
				{
					T item = default(T);
					var value = itemList.Get<string>(key, string.Empty);
					if (!string.IsNullOrEmpty(value))
					{
						if (IsSecured)
							item = ObjectSerialization.FromEncryptedString<T>(value, itemListKey);
						else
							item = ObjectSerialization.FromBinary<T>(Convert.FromBase64String(value));
					}
					if (null != item)
						items.Add(item.Name, item);
				}
			}
		}
		/// <summary>
		/// Saves any changes for the currently logged in user.
		/// </summary>
		public static void Save()
		{
			lock (itemsLock)
			{
				var itemList = new AppData();
				foreach (var item in items.Values)
				{
					Guard.NotNull(item.Name, Resources.AppUserConfiguration_Save_ItemMissingName);
					string key = HSS.IO.FileUtil.GetSafeFileName(item.Name.Replace(" ", ""));
					string value = null;
					if (IsSecured)
						value = item.ToEncryptedString(itemListKey);
					else
						value = Convert.ToBase64String(item.ToByteArray());
					if (null != value)
						itemList.Set<string>(key, value);
				}
				AppUserData.Set<AppData>(itemListKey, itemList);
				AppUserData.Save();
			}
		}
		/// <summary>
		/// Removes the specified item.
		/// </summary>
		/// <param name="name">The name of the item to remove.</param>
		/// <returns>true if the item was removed; otherwise false.</returns>
		public static bool Remove(string name)
		{
			bool result;
			lock (itemsLock)
				result = items.Remove(name);
			if (result && AutoSave)
				Save();
			return result;
		}
		/// <summary>
		/// Adds the specified item.
		/// </summary>
		/// <param name="item">The item to add.</param>
		public static bool Add(T item)
		{
			lock (itemsLock)
			{
				Guard.ArgumentNotNull(item, "item");
				Guard.ArgumentNotNullOrEmpty(item.Name, "item.Name");

				if (!items.ContainsKey(item.Name))
				{
					items.Add(item.Name, item);
					if (AutoSave)
						Save();
					return true;
				}
				return false;
			}
		}
		/// <summary>
		/// Determines if an item exists by the specified name.
		/// </summary>
		/// <param name="name">The name to check.</param>
		/// <returns>true if the item exists; otherwise false.</returns>
		public static bool Contains(string name)
		{
			lock (itemsLock)
				return items.ContainsKey(name);
		}
		/// <summary>
		/// Prompts the user to create a new item.
		/// </summary>
		/// <returns>The newly created item.</returns>
		public static T CreateNew()
		{
			return Edit(string.Empty);
		}
		/// <summary>
		/// Allows the user to edit the specified item.
		/// </summary>
		/// <param name="itemName">The name of the item to edit.</param>
		/// <returns>The modified item.</returns>
		public static T Edit(string itemName)
		{
			T item;
			items.TryGetValue(itemName, out item);
			if (null != Editor)
			{
				using (var editor = (IConfigurationDataEditor<T>)Activator.CreateInstance(Editor))
				{
					if (null != item)
						editor.LoadData(item);
					if (editor.ShowDialog() == DialogResult.OK)
					{
						lock (itemsLock)
						{
							items[itemName] = editor.GetData();
							return items[itemName];
						}
					}
				}
			}
			return default(T);
		}
		#endregion
	}
	#endregion
}